#pragma once

#include <Signals.h>
#include <string>
#include <Converter.h>
#include <CompilerFeatures.h>

#include <boost/type_traits/is_fundamental.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/noncopyable.hpp>
#include <boost/ptr_container/ptr_list.hpp>
#include <boost/function.hpp>

namespace liba {
namespace properties {

class AbstractDependencyProperty
{
public:
	virtual bool FromString(const std::string& value) = 0;
	virtual std::string ToString() = 0;
};

template<typename T, typename Enable = void>
class Constraint
{
public:
	virtual ~Constraint() { }
	virtual T Process(const T& value) = 0;
};

template<typename T>
class Constraint<T, typename boost::enable_if<boost::is_fundamental<T> >::type>
{
public:
	virtual ~Constraint() { }
	virtual T Process(T value) = 0;
};

template<typename T> 
class BaseDependencyProperty
	: public AbstractDependencyProperty
{
public:
	virtual T Get() const = 0;
	virtual void Set(T value) = 0;
};

template<typename T>
class DependencyPropertyByRef
	: public BaseDependencyProperty<T>
{
public:
	DependencyPropertyByRef() { }
	DependencyPropertyByRef(const T& value) : value(value) { }
	DependencyPropertyByRef(const DependencyPropertyByRef<T>& rhs)
		: value(rhs.value)
	{ }

	virtual T Get() const LIBA_OVERRIDE { return value; }
	const T& GetRef() const { return value; }
	virtual void Set(T value) LIBA_OVERRIDE 
	{ 
		if (!this->constraints.empty())
		{
			T cvalue = value;
			for (typename boost::ptr_list<Constraint<T> >::iterator i = this->constraints.begin(); i != this->constraints.end(); ++i)
				cvalue = i->Process(cvalue);
			this->value = cvalue;
		}
		else
		{
			this->value = value;
		}

		this->changed(this->value); 
	}

	liba::signals::signal<void (const T&)>& Changed() { return changed; }

	void AddConstraint(std::auto_ptr<Constraint<T> > value)
	{
		this->constraints.push_back(value);
	}
public:
	virtual bool FromString(const std::string& value) LIBA_OVERRIDE
	{
		return converter::convert(value, &this->value);
	}

	virtual std::string ToString() LIBA_OVERRIDE
	{
		std::string result;
		converter::convert(this->value, &result);
		return result;
	}
private:
	T value;
	liba::signals::signal<void (const T&)> changed;
	boost::ptr_list<Constraint<T> > constraints;
};

/** Specialization of dependency property for fundamental data types to avoid unnecessary references */
template<typename T>
class DependencyPropertyByValue
	: public BaseDependencyProperty<T>
{
public:
	DependencyPropertyByValue() { }
	DependencyPropertyByValue(T value) : value(value) { }
	DependencyPropertyByValue(const DependencyPropertyByValue<T>& rhs)
		: value(rhs.value)
	{ }

	virtual T Get() const LIBA_OVERRIDE { return value; }
	virtual void Set(T value) LIBA_OVERRIDE 
	{ 
		if (!this->constraints.empty())
		{
			T cvalue = value;
			for (typename boost::ptr_list<Constraint<T> >::iterator i = this->constraints.begin(); i != this->constraints.end(); ++i)
				cvalue = i->Process(cvalue);
			this->value = cvalue;
		}
		else
		{
			this->value = value; 
		}
		this->changed(this->value); 
	}

	liba::signals::signal<void (T)>& Changed() { return changed; }

	void AddConstraint(std::auto_ptr<Constraint<T> > value)
	{
		this->constraints.push_back(value);
	}
public:
	virtual bool FromString(const std::string& value) LIBA_OVERRIDE
	{
		return converter::convert(value, &this->value);
	}

	virtual std::string ToString() LIBA_OVERRIDE
	{
		std::string result;
		converter::convert(this->value, &result);
		return result;
	}
private:
	liba::signals::signal<void (T)> changed;
	T value;
	boost::ptr_list<Constraint<T> > constraints;
};

template<typename T, typename Enable = void>
struct DependencyProperty
	: DependencyPropertyByRef<T>
{
	DependencyProperty() { }
	DependencyProperty(const T& value) : Base(value) { }
	DependencyProperty(const DependencyProperty<T>& rhs)
		: Base(rhs)
	{ }

	const T& operator=(const T& value) { this->Set(value); return this->GetRef(); }
	operator const T&() const { return this->GetRef(); }

	bool operator== (const T& value) const { return this->GetRef() == value; }
	bool operator!= (const T& value) const { return !operator==(value); }

private:
	typedef DependencyPropertyByRef<T> Base;
};

template<typename T>
struct DependencyProperty<T, typename boost::enable_if<boost::is_fundamental<T> >::type>
	: DependencyPropertyByValue<T>
{
	DependencyProperty() { }
	DependencyProperty(const T& value) : Base(value) { }
	DependencyProperty(const DependencyProperty<T>& rhs)
		: Base(rhs)
	{ }

	T operator=(T value) { this->Set(value); return this->Get(); }
	operator T() const { return this->Get(); }

	T operator+=(T value) { this->Set(this->Get() + value); return this->Get(); }
	T operator-=(T value) { this->Set(this->Get() - value); return this->Get(); }
	T operator*=(T value) { this->Set(this->Get() * value); return this->Get(); }
	T operator/=(T value) { this->Set(this->Get() / value); return this->Get(); }

	bool operator== (T value) const { return this->Get() == value; }
	bool operator!= (T value) const { return !operator==(value); }
private:
	typedef DependencyPropertyByValue<T> Base;
};

template<typename T>
class VirtualDependencyPropertyByRef
	: public BaseDependencyProperty<T>
	, private boost::noncopyable
{
public:
	VirtualDependencyPropertyByRef(boost::function<const T& ()> getter, boost::function<void (const T&)> setter)
		: getter(getter)
		, setter(setter)
	{ }

	virtual T Get() const LIBA_OVERRIDE { return getter(); }
	virtual void Set(T value) LIBA_OVERRIDE { setter(value); }
public:
	virtual bool FromString(const std::string& value) LIBA_OVERRIDE
	{
		T convertedValue;
		bool res = converter::convert(value, &convertedValue);
		setter(convertedValue);
		return res;
	}

	virtual std::string ToString() LIBA_OVERRIDE
	{
		std::string result;
		converter::convert(getter(), &result);
		return result;
	}
private:
	boost::function<const T& ()> getter;
	boost::function<void (const T&)> setter;
};

template<typename T>
class VirtualDependencyPropertyByValue
	: public BaseDependencyProperty<T>
	, private boost::noncopyable
{
public:
	VirtualDependencyPropertyByValue(boost::function<T ()> getter, boost::function<void (T)> setter)
		: getter(getter)
		, setter(setter)
	{ }

	virtual T Get() const LIBA_OVERRIDE { return getter(); }
	virtual void Set(T value) LIBA_OVERRIDE { setter(value); }
public:
	virtual bool FromString(const std::string& value) LIBA_OVERRIDE
	{
		T convertedValue;
		bool res = converter::convert(value, &convertedValue);
		setter(convertedValue);
		return res;
	}

	virtual std::string ToString() LIBA_OVERRIDE
	{
		std::string result;
		converter::convert(getter(), &result);
		return result;
	}
private:
	boost::function<T ()> getter;
	boost::function<void (T)> setter;
};

template<typename T, typename Enable = void>
struct VirtualDependencyProperty
	: VirtualDependencyPropertyByRef<T>
{
	VirtualDependencyProperty(boost::function<const T& ()> getter, boost::function<void (const T&)> setter)
		: Base(getter, setter)
	{ }
	VirtualDependencyProperty(const DependencyProperty<T>& rhs)
		: Base(rhs)
	{ }
private:
	typedef VirtualDependencyPropertyByRef<T> Base;
};

template<typename T>
struct VirtualDependencyProperty<T, typename boost::enable_if<boost::is_fundamental<T> >::type>
	: VirtualDependencyPropertyByValue<T>
{
	VirtualDependencyProperty(boost::function<T ()> getter, boost::function<void (T)> setter)
		: Base(getter, setter)
	{ }
	VirtualDependencyProperty(const DependencyProperty<T>& rhs)
		: Base(rhs)
	{ }
private:
	typedef VirtualDependencyPropertyByValue<T> Base;
};

} // namespace properties
} // namespace liba